package com.starter.es.service.impl;


import cn.hutool.core.util.StrUtil;
import com.starter.es.common.SearchRequest;
import com.starter.es.common.FilterCommand;
import com.starter.es.common.GeoDistance;
import com.starter.es.service.SearchService;
import com.starter.es.utils.ToolUtils;
import java.io.IOException;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.join.query.HasChildQueryBuilder;
import org.elasticsearch.join.query.JoinQueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;

@Slf4j
public class SearchServiceImpl implements SearchService {


  private RestHighLevelClient client;

  public SearchServiceImpl(RestHighLevelClient client) {
    this.client = client;
  }

  @Override
  public SearchResponse query_string(SearchRequest request) {
    log.info("query_string__多字段搜索-普通分页, request:[ {} ]", request);
    org.elasticsearch.action.search.SearchRequest searchRequest = new org.elasticsearch.action.search.SearchRequest(request.getQuery().getIndexname());
    // 如果关键词为空，则返回所有
    String content = request.getQuery().getKeyWords();
    Integer rows = request.getQuery().getRows();
    if (rows == null || rows == 0) {
      rows = 10;
    }
    Integer start = request.getQuery().getStart();
    if (content == null || "".equals(content)) {
      // 查询所有
      content = "*";
    }
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    // 提取搜索内容
    BoolQueryBuilder builder;
    if ("*".equalsIgnoreCase(content)) {
      builder = QueryBuilders.boolQuery()
          .must(QueryBuilders.queryStringQuery(content).defaultOperator(Operator.AND));
    } else {
      builder = QueryBuilders.boolQuery().must(
          QueryBuilders.queryStringQuery(ToolUtils.handKeyword(content))
              .defaultOperator(Operator.AND));
    }
    // 提取过滤条件
    FilterCommand filter = request.getFilter();
    if (filter != null) {
      if (filter.getStartdate() != null && filter.getEnddate() != null) {
        builder.must(QueryBuilders.constantScoreQuery(
            QueryBuilders.rangeQuery(filter.getField()).from(filter.getStartdate())
                .to(filter.getEnddate())));
      }
    }
    // 排序
    if (StrUtil.isBlank(request.getQuery().getSort())) {
      searchSourceBuilder.sort(request.getQuery().getSort(), SortOrder.DESC);
    }

    searchSourceBuilder.query(builder);
    // 处理高亮
    HighlightBuilder highlightBuilder = new HighlightBuilder();
    highlightBuilder.field("*");
    searchSourceBuilder.highlighter(highlightBuilder);
    searchSourceBuilder.from(start);
    searchSourceBuilder.size(rows);

    searchRequest.source(searchSourceBuilder);
    SearchResponse searchResponse = null;
    try {
      searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
    } catch (IOException e) {
      e.printStackTrace();
    }
    log.info("query_string__多字段搜索-普通分页, request:[ {} ], searchResponse:[ {} ]", request,
        searchResponse);

    return searchResponse;
  }


  @Override
  public SearchResponse scrollquerystring(SearchRequest request) {

    log.info("scrollquerystring__多字段搜索-滚动分页, request:[ {} ] ", request);

    org.elasticsearch.action.search.SearchRequest searchRequest = new org.elasticsearch.action.search.SearchRequest(request.getQuery().getIndexname());
    // 如果关键词为空，则返回所有
    String content = request.getQuery().getKeyWords();
    Integer rows = request.getQuery().getRows();
    if (rows == null || rows == 0) {
      rows = 10;
    }
    Integer start = request.getQuery().getStart();
    if (content == null || "".equals(content)) {
      // 查询所有
      content = "*";
    }
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    // 提取搜索内容
    BoolQueryBuilder builder;
    if ("*".equalsIgnoreCase(content)) {
      builder = QueryBuilders.boolQuery()
          .must(QueryBuilders.queryStringQuery(content).defaultOperator(Operator.AND));
    } else {
      builder = QueryBuilders.boolQuery().must(
          QueryBuilders.queryStringQuery(ToolUtils.handKeyword(content))
              .defaultOperator(Operator.AND));
    }
    // 提取过滤条件
    FilterCommand filter = request.getFilter();
    if (filter != null) {
      if (filter.getStartdate() != null && filter.getEnddate() != null) {
        builder.must(QueryBuilders.constantScoreQuery(
            QueryBuilders.rangeQuery(filter.getField()).from(filter.getStartdate())
                .to(filter.getEnddate())));
      }
    }
    // 排序
    if (StrUtil.isBlank(request.getQuery().getSort())) {
      searchSourceBuilder.sort(request.getQuery().getSort(), SortOrder.ASC);
    }
    searchSourceBuilder.query(builder);
    // 处理高亮
    HighlightBuilder highlightBuilder = new HighlightBuilder();
    highlightBuilder.field("*");
    searchSourceBuilder.highlighter(highlightBuilder);

    searchSourceBuilder.size(rows);

    SearchResponse searchResponse = null;

    if (request.getQuery().getScrollid() == null) {
      searchSourceBuilder.from(0);
      searchRequest.scroll(TimeValue.timeValueMinutes(5L));
      searchRequest.source(searchSourceBuilder);
      try {
        searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
      } catch (IOException e) {
        e.printStackTrace();
      }
    } else {
      SearchScrollRequest scrollRequest = new SearchScrollRequest(request.getQuery().getScrollid());
      scrollRequest.scroll(TimeValue.timeValueMinutes(5L));
      try {
        searchResponse = client.scroll(scrollRequest, RequestOptions.DEFAULT);
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    log.info("scrollquerystring__多字段搜索-滚动分页, request:[ {} ], searchResponse:[ {} ]", request,
        searchResponse);

    return searchResponse;
  }

  @Override
  public SearchResponse termSearch(String index, String field, String term) {

    log.info("stermSearch__精准搜索, index:[ {} ], field:[ {} ], term:[ {} ]", index, field, term);

    org.elasticsearch.action.search.SearchRequest searchRequest = new org.elasticsearch.action.search.SearchRequest(index);
    BoolQueryBuilder builder = QueryBuilders.boolQuery().must(QueryBuilders.termQuery(field, term));
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    searchSourceBuilder.query(builder);
    searchRequest.source(searchSourceBuilder);
    SearchResponse searchResponse = null;
    try {
      searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    log.info("stermSearch__精准搜索, index:[ {} ], field:[ {} ], term:[ {} ], searchResponse:[ {} ]"
        , index, field, term, searchResponse);

    return searchResponse;
  }

  @Override
  public SearchResponse matchAllSearch(String index) {

    log.info("matchAllSearch__搜索全部, index:[ {} ] ", index);
    org.elasticsearch.action.search.SearchRequest searchRequest = new org.elasticsearch.action.search.SearchRequest(index);
    BoolQueryBuilder builder = QueryBuilders.boolQuery().must(QueryBuilders.matchAllQuery());
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    searchSourceBuilder.query(builder);
    searchRequest.source(searchSourceBuilder);
    SearchResponse searchResponse = null;
    try {
      searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    log.info("matchAllSearch__搜索全部, index:[ {} ] ,searchResponse:[ {} ]", index, searchResponse);

    return searchResponse;
  }


  @Override
  public SearchResponse geoDistanceSearch(String index, GeoDistance geo, Integer pagenum,
      Integer pagesize) {

    log.info("geoDistanceSearch__经纬度搜索, index:[ {} ] ,geo:[ {} ] ,pagenum:[ {} ] ,pagesize:[ {} ]"
        , index, geo, pagenum, pagesize);

    org.elasticsearch.action.search.SearchRequest searchRequest = new org.elasticsearch.action.search.SearchRequest("shop");
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    BoolQueryBuilder builder;
    builder = QueryBuilders.boolQuery().must(QueryBuilders.geoDistanceQuery("location")
        .point(geo.getLatitude(), geo.getLongitude())
        .distance(geo.getDistance(), DistanceUnit.KILOMETERS));
    SearchResponse searchResponse = null;
    try {
      searchSourceBuilder.query(builder);
      searchRequest.source(searchSourceBuilder);
      int start = (pagenum - 1) * pagesize;
      searchSourceBuilder.from(start);
      searchSourceBuilder.size(pagesize);
      searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    log.info(
        "geoDistanceSearch__经纬度搜索, index:[ {} ] ,geo:[ {} ] ,pagenum:[ {} ] ,pagesize:[ {} ] ,searchResponse:[ {} ]"
        , index, geo, pagenum, pagesize, searchResponse);

    return searchResponse;
  }

  @Override
  public SearchResponse matchNestedObjectSearch(String path, String index, String field,
      String value, Integer pagenum, Integer pagesize) {
    org.elasticsearch.action.search.SearchRequest searchRequest = new org.elasticsearch.action.search.SearchRequest(index);
    BoolQueryBuilder builder = QueryBuilders.boolQuery()
        .must(QueryBuilders
            .nestedQuery(path, QueryBuilders.matchQuery(field, value), ScoreMode.None));
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    searchSourceBuilder.query(builder);
    int start = (pagenum - 1) * pagesize;
    searchSourceBuilder.from(start);
    searchSourceBuilder.size(pagesize);
    // 处理高亮
    HighlightBuilder highlightBuilder = new HighlightBuilder();
    highlightBuilder.field("*");
    searchSourceBuilder.highlighter(highlightBuilder);
    searchRequest.source(searchSourceBuilder);
    SearchResponse searchResponse = null;
    try {
      searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    return searchResponse;
  }


  @Override
  public SearchResponse hasChildSearch(String childtype, String index, String field, String value,
      Integer pagenum, Integer pagesize) {
    org.elasticsearch.action.search.SearchRequest searchRequest = new org.elasticsearch.action.search.SearchRequest(index);
    HasChildQueryBuilder builder;
    if (value != null && !("".equals(value))) {
      builder = JoinQueryBuilders
          .hasChildQuery(childtype, QueryBuilders.termQuery(field, value), ScoreMode.None);
    } else {
      builder = JoinQueryBuilders
          .hasChildQuery(childtype, QueryBuilders.matchAllQuery(), ScoreMode.None);
    }
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    searchSourceBuilder.query(builder);
    int start = (pagenum - 1) * pagesize;
    searchSourceBuilder.from(start);
    searchSourceBuilder.size(pagesize);
    searchRequest.source(searchSourceBuilder);
    SearchResponse searchResponse = null;
    try {
      searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    return searchResponse;
  }


  @Override
  public SearchResponse hasParentSearch(String parenttype, String index, String field, String value,
      Integer pagenum, Integer pagesize) {
    org.elasticsearch.action.search.SearchRequest searchRequest = new org.elasticsearch.action.search.SearchRequest(index);
    QueryBuilder builder;
    if (value != null && !("".equals(value))) {
      builder = JoinQueryBuilders
          .hasParentQuery(parenttype, QueryBuilders.termQuery(field, value), false);
    } else {
      builder = JoinQueryBuilders.hasParentQuery(parenttype, QueryBuilders.matchAllQuery(), false);
    }
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    searchSourceBuilder.query(builder);
    int start = (pagenum - 1) * pagesize;
    searchSourceBuilder.from(start);
    searchSourceBuilder.size(pagesize);
    searchRequest.source(searchSourceBuilder);
    SearchResponse searchResponse = null;
    try {
      searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    return searchResponse;
  }
}
